#!/usr/bin/env.python
# _*_ coding:utf-8 _*_
# # # # # # # # # # # # # # # # # # # #
#  题目:      booktest的序列化器        #
#  姓名:         李吉辉                #
#  时间:   2018年12月15日16:21:56      #
# # # # # # # # # # # # # # # # # # # #

from rest_framework import serializers

from .models import BookInfo

"""
主要区别: 
            1. Serializer需要定义模型,而ModelSerializer直接把模型映射,不要再定义了.
            2. Serializer中只定义模型的更新和新增,没有保存到数据库的实现,而ModelSerializer自带更新和新增的实现
            
注意点: 
        1. 当对父类需求的少推荐使用Serializer,因为ModelSerializer集成性高,好比杀鸡焉用牛刀.--> 性能方面的问题
        2. 主键和外键都要只读 read_only=True
"""


"""ModelSerializer序列化器"""
class BookInfoModelSerializer(serializers.ModelSerializer):
    """自定义序列化器,继承model序列化器"""

    class Meta:
        model = BookInfo  # 给序列化器指定字段从那个模型去映射
        fields = "__all__"   # 指定需要从模型中映射那些字段(__all__表示取所有)
        # fields = ["id", "btitle"]  # 自定义从模型中映射的字段

        exclude = ['image']  # 排除image其它全要

        extra_kwargs = {  # 给某些字段进行选项修改和指定
            # 真正的原理是 后写的覆盖前写的
            'bread': {'min_value': 0, "read_only": True},
            'bcomment': {'min_value': 0, },
        }

        # 指定只出输出的字典（只会序列化）
        # read_only_fields = ['bcomment']




"""Serializer序列化器"""
class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)  # read_only: True-->只读, 只能序列化
    btitle = serializers.CharField(label='名称', max_length=20)
    bpub_date = serializers.DateField(label='发布日期', required=True)  # required: True-->必须传的; False-->不是必须传的
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)

    # 注意点: 如果在一的序列器中要序列化多的那一方时,要多加 many=true
    # heroinfo_set = serializers.PrimaryKeyRelatedField(label='英雄', read_only=True, many=True)
    heroinfo_set = serializers.StringRelatedField(label='英雄', read_only=True, many=True)  # 序列化出外键对象的str方法返回值
    # heroinfo_set = HeroInfoSerializer()  # 在序列化外键时,会把外键对应的序列化器中的所有数据充列化出来

    # 下面的校验是在调用 views视图函数里面 serializer.is_valid(raise_exception=True) 之后调用的
    """对【单一字段】进行额外追加校验逻辑时 方法名: validate_字段名"""
    def validate_btitle(self, value):
        # 先把传入的值进行小写化, 再判断django字符串是否存在于值里面
        if "django" not in value.lower() :
            # 抛出异常
            raise serializers.ValidationError("图书不是关于Django的")
        return value  # 注意, 如果校验Ok,就把原来的值返回,返回到serializer.validated_data的字典中

        # 单个字段会告诉你那个字段错误: rest_framework.exceptions.ValidationError: {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}

    """对【多个字段】进行额外联合校验逻辑时 方法名: validate"""
    def validate(self, attrs):  # 多个字段联合校验
        # 取serializer.validated_data的字典中的值
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        # 判断阅读量是否小于评论量
        if bread < bcomment:
            # 抛出异常
            raise serializers.ValidationError('阅读量小于评论量')
        return attrs  # 还是返回到serializer.validated_data的字典中


    # 因为Serializer继承的这个爹只有定义更新和新增的函数, 没有保存到数据库的能力,所以要自己重写
    # 根据 instance(实例)是否有值来判断, 有值表示更新, 反之新增
    def create(self, validated_data):
        """新建"""
        return BookInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新，instance为要更新的对象实例"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance




class HeroInfoSerializer(serializers.Serializer):
    """英雄数据序列化器"""
    GENDER_CHOICES = (
        (0, 'female'),
        (1, 'male')
    )
    id = serializers.IntegerField(label='ID', read_only=True)
    hname = serializers.CharField(label='名字', max_length=20)
    hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性别', required=False)
    hcomment = serializers.CharField(label='描述信息', max_length=200, required=False, allow_null=True)

    # 外键查询三种方法 --> 常用是第一种
    # hbook = serializers.PrimaryKeyRelatedField(label='书籍', read_only=True)  # 只序列化出外键的id
    # hbook = serializers.StringRelatedField(label='书籍', read_only=True)  # 序列化出外键对象的str方法返回值
    hbook = BookInfoSerializer()  # 在序列化外键时,会把外键对应的序列化器中的所有数据充列化出来

"""
注意点: 如果使用第三种方法需要 需要在外键对象的类后面, 否则无法创建实例序列化器
"""